package com.zkh.myframe.core;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.zkh.myframe.enumeration.adapter.Scope;
import com.zkh.myframe.enumeration.adapter.SendType;
import com.zkh.myframe.exception.BaseException;
import com.zkh.myframe.interceptor.UploadFileInterceptor;
import com.zkh.myframe.validate.ParamValidator;
import com.zkh.myutils.bean.BeanUtils;
import com.zkh.myutils.code.CoderUtils;
import com.zkh.myutils.image.CodeType;
import com.zkh.myutils.image.VerifyCode;
import com.zkh.myutils.io.file.FileUtils;
import com.zkh.myutils.io.log.Logger;
import com.zkh.myutils.json.JSON;
import com.zkh.myutils.json.JSONArray;
import com.zkh.myutils.json.JSONObject;
import com.zkh.myutils.net.upload.UploadFile;
import com.zkh.myutils.net.upload.Uploader;
import com.zkh.myutils.utils.WebUtils;
import com.zkh.myutils.utils.Assert;
import com.zkh.myutils.utils.StringUtils;

/**
 * 适配器类，整个项目的Controller都使用该适配器来贯穿。<br />
 * 核心功能：<br /><b>
 *    1、用于获取HttpServletRequest对象和HttpServletResponse对象。<br />
 *    2、统一页面跳转类型，统一结果集。可设置转发的前后缀。<br />
 *    3、保存、获取解析的参数。</b>
 * @author zkh
 *
 */
public final class Adapter<D extends ParamValidator> {
	/**
	 * 构造方法
	 */
	public Adapter(HttpServletRequest request, HttpServletResponse response){
		this.request = request;
		this.response = response;
	}

	/************   私有属性       *************/
	private HttpServletRequest request;
	private HttpServletResponse response;
	//参数映射
	private Map<String, List<String>> argumentMap = new HashMap<String, List<String>>();
	//JSON格式
	private JSON argumentJSON;
	private String argumentJSONString;
	//转发时候的前缀和后缀
	private String prefix = null;
	private String suffix = null;
	//cookie容器
	private Map<String, String> cookies;
	//参数验证器
	private D paramValidator;
	//参数验证器Class
	private Class<D> validatorClass;
	//请求类型
	private String requestMethod;
	//参数验证器验证不通过的错误信息
	Map<String, String> validateErrorMsg = new HashMap<String, String>();
	//是否忽略值为null的key（转JSON时）
	private static boolean ignoreNullValueKey = "true".equals(Configure.getValue("ignoreNullValueKey"));
	
	/**
	 * 发送类型
	 */
	private SendType sendType = SendType.DISPATCHER;
	/**
	 * 返回值。字符串为跳转，JSON对象直接输出。
	 */
	private Object result = null;
	
	/**
	 * 提供一个Map，供Controller的方法使用
	 */
	private Map<String, Object> map = new HashMap<String, Object>();
	
	/**
	 * 获取request
	 * @return
	 */
	public HttpServletRequest getRequest() {
		return request;
	}
	/**
	 * 获取response
	 * @return
	 */
	public HttpServletResponse getResponse() {
		return response;
	}
	/**
	 * 获取session
	 * @return
	 */
	public HttpSession getSession(){
		return request.getSession();
	}
	
	/**
	 * 设置值，将属性设置在request域中
	 * @param attr 属性名
	 * @param obj 属性值
	 */
	public void set(String attr, Object obj){
		request.setAttribute(attr, obj);
	}
	
	/**
	 * 将属性设置在指定域中，如果域不在三大常量中，则默认request域
	 * @param attr 属性名
	 * @param obj 属性值
	 * @param scope 域
	 */
	public void set(String attr, Object obj, Scope scope){
		//根据不同的域设置值，如果不在三个域中，则默认request域
		switch(scope){
			case SESSION:
				request.getSession().setAttribute(attr, obj);
				break;
			case APPLICATION:
				request.getServletContext().setAttribute(attr, obj);
				break;
			default:
				request.setAttribute(attr, obj);
		}
	}
	
	/**
	 * 从Request域中获取字符串
	 * @param attr 属性名
	 * @return
	 */
	public String get(String attr){
		return get(attr, String.class, Scope.REQUEST);
	}
	
	/**
	 * 在Request域中读取属性值
	 * @param attr 属性名
	 * @param clazz 获取的数据要转换的类型
	 * @return
	 */
	public <T> T get(String attr, Class<T> clazz){
		return get(attr, clazz, Scope.REQUEST);
	}
	
	/**
	 * 在指定域中读取属性值
	 * @param attr 属性名
	 * @param clazz 获取的数据要转换的类型
	 * @param scope 域
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> T get(String attr, Class<T> clazz, Scope scope){
		//在不同的域中取值
		switch(scope){
			case SESSION:
				return (T) getSession().getAttribute(attr);
			case APPLICATION:
				return (T) request.getServletContext().getAttribute(attr);
			default:
				return (T) request.getAttribute(attr);
		}
	}
	
	/**
	 * 处理结果，根据属性分别执行跳转、转发、发送JSON数据等操作。由Controller适配器自动调用。
	 */
	public void doResult(){
		//不为null
		if(result!=null){
			//字符串类型，转发或跳转
			if(result instanceof String){
				try{
					//处理结果
					if(sendType==SendType.DISPATCHER){
						//转发路径
						String path = (prefix==null?Configure.getPrefix():prefix)+result+(suffix==null?Configure.getSuffix():suffix);
						//转发
						request.getRequestDispatcher(path).forward(request, response);
					}else if(sendType==SendType.REDIRECT){
						//重定向（http开头直接重定向，否则本地重定向）
						response.sendRedirect((String) result);
					}else if(sendType==SendType.HTML){
						//直接输出
						WebUtils.sendHtml(response, result.toString());
					}else if(sendType==SendType.HTML_PHONE){
						//输出到移动设备
						WebUtils.sendPhoneHtml(response, result.toString());
					}else if(sendType==SendType.TEXT) {
						//直接输出
						WebUtils.sendText(response, result.toString());
					}
				}catch(IOException | ServletException e){
					throw new RuntimeException(e);
				}
			}//整型，发送错误码
			else if(result instanceof Integer){
				try {
					if(sendType==SendType.HTML){
						//直接输出
						WebUtils.sendHtml(response, result.toString());
					}else if(sendType==SendType.TEXT) {
						//直接输出
						WebUtils.sendText(response, result.toString());
					}else if(sendType==SendType.HTML_PHONE){
						//输出到移动设备
						WebUtils.sendPhoneHtml(response, result.toString());
					}else{
						response.sendError((Integer) result);
					}
				} catch (IOException e) {
					Logger.error(e);
				}
			}else{
				//非字符串类型，发送JSON数据
				if(sendType==SendType.HTML){
					//发送text/html类型的JSON数据
					WebUtils.sendHtmlWithJSON(response, result, ignoreNullValueKey);
				}else if(sendType==SendType.TEXT) {
					//发送text/plain类型的JSON数据
					WebUtils.sendTextWithJSON(response, result, ignoreNullValueKey);
				}else{
					//发送application/json类型的JSON数据
					WebUtils.sendJson(response, result, ignoreNullValueKey);
				}
			}
		}
	}
	
	/**
	 * 获取参数验证器对象，其中的数据已经通过验证
	 * @return
	 */
	public D getParamValidator() {
		return paramValidator;
	}
	
	/**
	 * 检查请求方法
	 * @param methodType 请求方法。post，get等类型，不区分大小写。
	 * @return 如果是指定类型，返回true，否则返回false。
	 */
	public boolean isTargetMethod(String methodType) {
		if(requestMethod==null) {
			requestMethod = request.getMethod();
		}
		return requestMethod.equalsIgnoreCase(methodType);
	}
	
	/**
	 * 获取参数验证器验证不通过的错误信息。<br/>
	 * 其中，key为自定义验证器的字段名，value为对应字段验证不通过的错误信息。
	 * 
	 * @return
	 */
	public Map<String, String> getParamValidateErrorMsg(){
		return validateErrorMsg;
	}
	
	/**
	 * 获取参数。为兼容复选框，所有参数都是通过数组来存放
	 * @param key 键
	 * @return
	 */
	public String[] getParameters(String key) {
		return Assert.getIfNotNull(argumentMap.get(key), (m)->{
			return m.toArray(new String[m.size()]);
		});
	}
	
	/**
	 * 获取第一个参数。为兼容复选框，所有参数都是通过数组来存放
	 * @param key 键
	 * @return
	 */
	public String getParameter(String key) {
		return getParameter(key, null);
	}
	
	/**
	 * 获取解析出来的数组参数
	 * @return
	 */
	public String[] getParameterParse() {
		return getParameters("arguments_myframe");
	}
	
	/**
	 * 获取第一个参数。为兼容复选框，所有参数都是通过数组来存放
	 * @param key 键
	 * @param defVal 默认值
	 * @return
	 */
	public String getParameter(String key, String defVal) {
		return Assert.getIfNotNull(argumentMap.get(key), (list)->list.size()==0?defVal:list.get(0), defVal);
	}
	
	/**
	 * 获取long型参数
	 * @param key 参数名
	 * @return
	 */
	public double getParameterNumber(String key){
		return getParameterNumber(key, 0);
	}
	
	/**
	 * 获取long型参数，并指定默认值
	 * @param key 参数名
	 * @param dftValue 默认值
	 * @return
	 */
	public double getParameterNumber(String key, double dftValue){
		//获取值
		String v = getParameter(key);
		//处理并返回
		return StringUtils.isNumeric(v) ? Double.parseDouble(v) : dftValue;
	}

	/**
	 * 获取解析出来的所有参数
	 * @return
	 */
	public Map<String, String[]> getParameterMap(){
		//容器
		Map<String, String[]> ctn = new HashMap<>();
		//组装
		argumentMap.forEach((k, v)->ctn.put(k, v==null?null:v.toArray(new String[v.size()])));
		//返回
		return ctn;
	}
	
	/**
	 * 将参数自动转换为实体
	 * @param entityClass 实体Class
	 * @return
	 */
	public <T> T getParameterEntity(Class<T> entityClass) {
		return BeanUtils.mapToBean(getParameterMap(), entityClass);
	}
	
	/**
	 * 是否传递的JSON参数
	 * @return
	 */
	public boolean isJSONParam() {
		return argumentJSONString!=null;
	}
	
	/**
	 * 从流中以UTF-8编码获取实体对象（流中存储的是JSON字符串，直接转换为实体对象）
	 * @param clazz 实体类Class
	 * @return
	 */
	public <T> List<T> getJsonParamToList(Class<T> clazz) {
		//获取JSON对象
		JSON json = getJSONFromParamStream();
		//List类型的对象
		if(json instanceof JSONArray) {
			return ((JSONArray) json).parseList(clazz);
		}
		return new ArrayList<T>();
	}
	
	/**
	 * 从流中以UTF-8编码获取实体对象（流中存储的是JSON字符串，直接转换为实体对象）
	 * @param clazz 实体类Class
	 * @return
	 */
	public <T> T getJsonParamToEntity(Class<T> clazz) {
		//获取JSON对象
		JSON json = getJSONFromParamStream();
		//List类型的对象
		if(json instanceof JSONObject) {
			return ((JSONObject) json).parseEntity(clazz);
		}
		try {
			return clazz.newInstance();
		} catch (InstantiationException | IllegalAccessException e) {
			throw new BaseException("对象转换失败", e);
		}
	}
	
	/**
	 * 获取Cookie
	 * @param key
	 * @return
	 */
	public String getCookie(String key){
		return getCookies().get(key);
	}
	
	/**
	 * 获取cookie
	 * @return
	 */
	public Map<String, String> getCookies(){
		//未初始化，则获取cookie
		if(cookies==null){
			//初始化
			cookies = new HashMap<String, String>();
			//获取Cookie
			Cookie[] cks = request.getCookies();
			//有值
			if(cks!=null){
				for(Cookie c: cks){
					cookies.put(c.getName(), CoderUtils.decodeURL(c.getValue()));
				}
			}
		}
		return cookies;
	}
	
	/**
	 * 添加Cookie，默认path=/，关闭浏览器失效，域名默认
	 * @param key cookie键
	 * @param value cookie值
	 */
	public void addCookie(String key, String value) {
		addCookie(key, value, "/", null, null);
	}
	
	/**
	 * 添加Cookie，默认path=/，域名默认
	 * @param key cookie键
	 * @param value cookie值
	 * @param maxAge 过期时间
	 */
	public void addCookie(String key, String value, int maxAge) {
		addCookie(key, value, "/", maxAge, null);
	}
	
	/**
	 * 添加Cookie
	 * @param key cookie键
	 * @param value cookie值
	 * @param path 路径
	 * @param maxAge 过期时间
	 */
	public void addCookie(String key, String value, String path, Integer maxAge) {
		addCookie(key, value, path, maxAge, null);
	}
	
	/**
	 * 添加Cookie
	 * @param key cookie键
	 * @param value cookie值
	 * @param path 路径
	 * @param maxAge 过期时间
	 * @param domain 域名
	 */
	public void addCookie(String key, String value, String path, Integer maxAge, String domain) {
		//新建Cookie，值使用URL编码，否则包含特殊字符时，浏览器不会发送
		Cookie cookie = new Cookie(key, CoderUtils.encodeURL(value));
		//设置属性
		Assert.doIfNotEmpty(path, (p)->cookie.setPath(p));
		Assert.doIfNotNull(maxAge, (a)->cookie.setMaxAge(a));
		Assert.doIfNotEmpty(domain, (d)->cookie.setDomain(d));
		//添加Cookie
		response.addCookie(cookie);
	}
	
	/**
	 * 移除指定Cookie
	 * @param key
	 */
	public void removeCookie(String key) {
		//存在Cookie，则移除
		if(getCookies().containsKey(key)) {
			addCookie(key, "", -1);
		}
	}
	
	/**
	 * 移除所有Cookie
	 */
	public void removeAllCookies() {
		getCookies().keySet().forEach(k->addCookie(k, "", -1));
	}
	
	/**
	 * 获取相对于项目根目录的目录或文件的绝对路径
	 * @param path 目录名或文件名(相对项目根目录)
	 * @return
	 */
	public String getRealPath(String pathFile){
		if(pathFile==null){
			return null;
		}
		//支持开头的/
		if(!pathFile.startsWith("/")){
			pathFile = "/" + pathFile;
		}
		//项目的绝对路径(兼容Servlet3以下版本)
		return request.getSession().getServletContext().getRealPath(pathFile);
	}
	
	/**
	 * 获取上传文件的异常信息。如果异常信息为<b>null</b>，说明上传成功，否则上传失败。
	 * @return
	 */
	public String getUploadExceptionMessage(){
		//查看是否有异常信息
		String uploadException = (String) request.getAttribute(UploadFileInterceptor.UPLOAD_FILE_LIST_EXCEP);
		//如果存在异常，则抛出异常信息
		return uploadException;
	}
	
	/**
	 * 上传文件是否成功
	 * @return 非文件上传或上传成功，均返回true
	 */
	public boolean isUploadSuccess(){
		return getUploadExceptionMessage()==null;
	}
	
	/**
	 * 获取上传进度，取值在[0, 1]之间。<b>不传key值，将从进度池中随机取一个进度。如果能确保一个账号不会存在同一时间有多个上传请求，可使用该方法。</b>
	 * @return
	 */
	public double getUploadProgress() {
		return Uploader.getProgress(request);
	}
	
	/**
	 * 获取上传进度，取值在[0, 1]之间。
	 * @param key 上传对象唯一标识
	 * @return
	 */
	public double getUploadProgress(String key) {
		return Uploader.getProgress(request, key);
	}
	
	/**
	 * 清除指定上传进度信息
	 * @param key 上传对象唯一标识
	 */
	public void clearUploadProgress(String key) {
		Uploader.clearProgress(request, key);
	}
	
	/**
	 * 清除所有上传进度信息（当前session）
	 */
	public void clearAllUploadProgress() {
		Uploader.clearProgressAll(request);
	}
	
	/**
	 * 获取上传文件对象
	 * @return
	 */
	public List<UploadFile> getAllUploadFiles(){
		//容器
		List<UploadFile> ctn = new ArrayList<>();
		//添加数据
		getUploadFileMap().values().forEach(ctn::addAll);
		//返回集合对象
		return ctn;
	}
	
	/**
	 * 根据表单name字段的值获取上传文件。（单文件上传）
	 * @param name
	 * @return
	 */
	public UploadFile getUploadFile(String name){
		//上传文件
		List<UploadFile> files = getUploadFiles(name);
		//返回第一个
		return files.size()==0 ? null : files.get(0);
	}
	
	/**
	 * 根据表单name字段的值获取上传文件。（多文件上传）
	 * @param name
	 * @return
	 */
	public List<UploadFile> getUploadFiles(String name){
		return getUploadFileMap().getOrDefault(name, new ArrayList<>());
	}
	
	/**
	 * 获取上传文件时的表单值。多值表单（如复选框）仅能获取第一个值。
	 * @param key 表单name属性值
	 * @return
	 */
	public String getUploadParameter(String key){
		//获取值
		String[] vs = getUploadParameters(key);
		//返回值
		return vs==null || vs.length==0 ? null : vs[0];
	}
	
	/**
	 * 获取上传文件时的多值表单值（如复选框）。
	 * @param key 表单name属性值
	 * @return
	 */
	public String[] getUploadParameters(String key){
		//返回值
		return getUploadParameterMap().get(key);
	}
	
	/**
	 * 获取所有上传参数
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public Map<String, String[]> getUploadParameterMap(){
		return (Map<String, String[]>) request.getAttribute(UploadFileInterceptor.UPLOAD_FORM_FIELDS);
	}
	
	/**
	 * 将上传文件保存到配置目录下。自动检测是流还是临时文件。<br />
	 * <b>注：配置的文件上传根目录只有在有文件上传时才会被读取！</b>
	 * @param uploadFile 上传文件对象
	 * @param filename 保存的文件名
	 */
	public void saveUploadFileTo(UploadFile uploadFile, String filename){
		saveUploadFileTo(uploadFile, UploadFileInterceptor.uploaderConfig.getBaseDir(), filename);
	}
	
	/**
	 * 将上传文件保存到指定目录下。自动检测是流还是临时文件。
	 * @param uploadFile 上传文件对象
	 * @param path 保存的目录，磁盘绝对位置
	 * @param filename 保存的文件名
	 */
	public void saveUploadFileTo(UploadFile uploadFile, String path, String filename){
		//流
		if(uploadFile.getFileStream()!=null) {
			FileUtils.saveInputStreamTo(uploadFile.getFileStream(), path, filename);
		}
		//临时文件
		if(uploadFile.getFile()!=null) {
			//复制
			FileUtils.copy(uploadFile.getFile(), new File(path, filename));
			//删除临时文件
			FileUtils.delete(uploadFile.getFile());
		}
	}
	
	/**
	 * 输出默认验证码。<br />
	 * <b>验证验证码是否正确请调用 isVerifyCodeRight(String verifycode) 方法。</b>
	 * @param width 验证码宽
	 * @param height 验证码高
	 * @param useChinese 是否使用中文验证码
	 */
	public void drawVerifyCode(int width, int height, boolean useChinese){
		//实例化验证码
		VerifyCode code = new VerifyCode(width, height, useChinese?CodeType.CHINESE:CodeType.LETTER_NUMBER);
		//增加中文字库
		if(useChinese) {
			code.setFontlib("春集丈木研班普导顿睡展跳获艺六波察群皇段急庭创" + 
					"区奥器谢弟店否害草排背止组州朝封睛板角况曲馆育" + 
					"忙质河续哥呼若推境遇雨标姐充围案伦护冷警贝著雪" + 
					"索剧啊船险烟依斗值帮汉慢佛肯闻唱沙局伯族低玩资" + 
					"屋击速顾泪洲团圣旁堂兵七露园牛哭旅街劳型烈姑陈" + 
					"莫鱼异抱宝权鲁简态级票怪寻杀律胜份汽右洋范床舞" + 
					"秘午登楼贵吸责例追较职属渐左录丝牙党继托赶章智");
		}
		//显示验证码
		try {
			//生成验证码
			BufferedImage image = code.create();
			//保存验证码信息
			code.saveToSession(getSession());
			//输出图片
			code.output(image, response.getOutputStream());
		} catch (IOException e) {
			throw new RuntimeException();
		}
	}
	
	/**
	 * 验证码是否正确
	 * @param verifycode
	 * @return
	 */
	public boolean isVerifyCodeRight(String verifycode){
		return verifycode==null ? false : VerifyCode.checkCode(getSession(), verifycode);
	}
	
	/**
	 * 清除验证码
	 */
	public void clearVerifyCode() {
		VerifyCode.clearSession(getSession());
	}
	
	
	/*********  getter和setter   ************/
	
	/**
	 * 临时设置转发前缀，默认  /WEB-INF/content/
	 * @param prefix
	 */
	public void setPrefix(String prefix) {
		this.prefix = prefix;
	}
	/**
	 * 临时设置转发后缀，默认 .jsp
	 * @param suffix
	 */
	public void setSuffix(String suffix) {
		this.suffix = suffix;
	}
	/**
	 * 临时设置转发的前后缀
	 * @param prefix 转发前缀，默认  /WEB-INF/content/
	 * @param suffix 转发后缀，默认 .jsp
	 */
	public void setPreSuffix(String prefix, String suffix){
		setPrefix(prefix);
		setSuffix(suffix);
	}
	/**
	 * 设置结果。<br />
	 *  1、如果参数为字符串，则默认为转发；<br />
	 *  2、如果参数为整型，则默认该参数为错误码，直接返回错误码；<br />
	 *  3、如果不是字符串和整型，则统一发送application/json类型的JSON数据。<br />
	 *  <b>其它发送方式请调用 setResult(result, sendType)方法进行设置</b>
	 * @param result 结果
	 */
	public void setResult(Object result) {
		this.result = result;
	}
	/**
	 * 设置结果。
	 * <pre>
	 * 当result为字符串时，sendType可取值：<b>（默认转发）</b>
	 * 	DISPATCHER 	//转发
	 *	REDIRECT	//重定向
	 *	HTML		//以text/html方式发送文本
	 *	HTML_PHONE	//以text/html方式发送移动端文本
	 *	TEXT		//以text/plain方式发送文本
	 *
	 * 当results为整型时，sendType可取值：<b>（默认发送错误码）</b>
	 *	HTML		//以text/html方式发送文本
	 *	HTML_PHONE	//以text/html方式发送移动端文本
	 *	TEXT		//以text/plain方式发送文本
	 *
	 * 当results为非字符串和非整型时，sendType可取值：<b>（默认以application/json方式发送JSON对象）</b>
	 * HTML		//以text/html方式发送JSON字符串
	 * TEXT		//以text/plain方式发送文本
	 * </pre>
	 * @param result 结果对象
	 * @param sendType 发送类型，使用枚举类SendType获取。
	 */
	public void setResult(Object result, SendType sendType) {
		this.result = result;
		this.sendType = sendType;
	}
	/**
	 * 将内置Map保存到结果中
	 */
	public void setResultWithMap() {
		this.result = getMap();
	}
	/**
	 * 发送类型。当以返回值方式（而非setResult方式）返回结果时，可通过该方法指定发送类型。当然，也可以配合setResult使用。
	 * @param sendType 发送类型，使用枚举类SendType获取。
	 */
	public void setSendType(SendType sendType) {
		this.sendType = sendType;
	}
	/**
	 * 获取内置的Map容器，控制器的方法可直接使用。
	 * @return
	 */
	public Map<String, Object> getMap() {
		return map;
	}
	/**
	 * 给内置的Map容器(即通过getMap()方法获取到的Map对象)添加数据
	 * @param key 键
	 * @param value 值
	 */
	public void putMap(String key, Object value){
		map.put(key, value);
	}
	
	/**
	 * 实例化一个新的HashMap
	 * @return
	 */
	public Map<String, Object> createHashMap(){
		return new HashMap<String, Object>();
	}
	
	
	/*********** 供系统调用的方法  ****************/
	/**
	 * 框架解析参数时调用
	 * @param key
	 * @param value
	 */
	void setParameter(String key, List<String> value) {
		argumentMap.put(key, value);
	}
	/**
	 * 框架解析参数时调用
	 * @param key
	 * @param value
	 */
	void addParameter(String key, String value) {
		//获取容器
		List<String> ctn = argumentMap.get(key);
		//不存在，初始化
		if(ctn==null) {
			argumentMap.put(key, ctn = new ArrayList<>());
		}
		//保存
		ctn.add(value);
	}
	/**
	 * 从流中保存json字符串
	 * @param argumentJSONString
	 */
	void setArgumentJSONString(String argumentJSONString) {
		this.argumentJSONString = argumentJSONString;
	}
	/**
	 * 设置验证器实例对象
	 */
	void setParamValidatorClass(Class<D> paramValidatorClass) {
		validatorClass = paramValidatorClass;
	}
	/**
	 * 设置验证器实例对象
	 */
	void setParamValidator(D paramValidator) {
		this.paramValidator = paramValidator;
	}
	/**
	 * 获取验证器Class
	 * @return
	 */
	Class<D> getValidatorClass(){
		return validatorClass;
	}
	
	

	/********** 内部调用的方法  **********/
	/**
	 * 从请求流中获取JSON对象（流中存储的是JSON字符串）
	 * @return
	 */
	private JSON getJSONFromParamStream() {
		//已存在，直接返回
		if(argumentJSON==null) {
			//为空
			if(StringUtils.isEmpty(argumentJSONString)) {
				argumentJSON = new JSONObject(null);
			}else {
				//解析
				argumentJSON = JSON.getJSON(argumentJSONString);
			}
		}
		return argumentJSON;
	}
	
	/**
	 * 获取上传文件映射
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private Map<String, List<UploadFile>> getUploadFileMap() {
		//获取集合对象
		Map<String, List<UploadFile>> uploadFileMap = (Map<String, List<UploadFile>>) request.getAttribute(UploadFileInterceptor.UPLOAD_FORM_FILES);
		//返回
		return uploadFileMap==null ? new HashMap<>() : uploadFileMap;
	}
}
